package com.mikgonz.farkle;

import java.util.ArrayList;
import java.util.Iterator;

import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.view.View;

import com.mikgonz.farkle.ui.GameScreen;

public class LocalGameController {

	private GameScreen UI;
	private DieManager dM;

	private ArrayList<Player> players = new ArrayList<Player>();
	private Iterator<Player> iterator = players.iterator();
	private Player currPlayer;

	private int WINNING_SCORE;

	private ArrayList<GameEventsListener> listeners = new ArrayList<GameEventsListener>();

	private boolean rollState = false;
	private boolean scoreState = false;

	public enum GameState {
		PLAYER_CONTROLLING, WAITING_FOR_NEXT, LAST_CHANCE
	}

	private GameState gameState;

	public LocalGameController(GameScreen UI) {
		this.UI = UI;
		dM = new DieManager();
		setup();
		updateScoreBox();
		setState(GameState.WAITING_FOR_NEXT);

		// The flow begins either from onRoll(), onScore(),
		// onClickDice(), onAnimationEnd()
	}

	private void setState(GameState gs) {
		gameState = gs;

		switch (gs) {
		case WAITING_FOR_NEXT:
			startRound();
			break;

		case PLAYER_CONTROLLING:
			if (isLeaderPastWinningScore()) {
				setState(GameState.LAST_CHANCE);
			}
			break;

		}

	}

	private void endRound() {

		currPlayer.incrementScore(currPlayer.getInRoundScore());
		currPlayer.resetInRoundScore();

		dM.clearTable();
		UI.updateImages();
		updateScoreBox();

		setState(GameState.WAITING_FOR_NEXT);
	}

	private void startRound() {

		if (!iterator.hasNext())
			iterator = players.iterator();

		currPlayer = iterator.next();

		if (currPlayer.isOriginalWinner())
			UI.alertOfWinner(currPlayer);

		updateScoreBox();
	}

	public void onClickDice(int index) {

		if (gameState == GameState.WAITING_FOR_NEXT)
			return;

		int value = dM.getValue(index, DieManager.ABS_VALUE_FLAG);

		// A letter was clicked
		if (value == 0)
			return;

		// Always highlight 5's and 1's
		if (value == 5 || value == 1) {

			dM.toggleHighlight(index);

		} else if (shouldHighlight(index)) {

			int[] indexOfPairs = dM.findPairs(index, DieManager.INDEX_FLAG,
					false);
			for (int i : indexOfPairs) {
				dM.toggleHighlight(i);
			}

		}

		updateButtonState();
		UI.updateImages();

	}

	private void updateButtonState() {
		// The score of the highlighted dice
		final int[] highlightedDice = dM
				.getHighlightedDice(DieManager.ABS_VALUE_FLAG);
		int highlightedScore = Scorer.calculate(highlightedDice, false, UI);
		// Second (boolean) parameter is false to not ignore any extra dice

		if (highlightedScore == 0) {
			UI.setRollButtonState(false);
			UI.setScoreButtonState(false);
			return;
		}

		// Player can PickUp the dice and roll again
		UI.setRollButtonState(true);

		// This next part checks if the player can score
		final int scoreOfLeader = getLeader().getScore();
		final int possibleScoreOfCurrPlayer = currPlayer.getInRoundScore()
				+ highlightedScore + currPlayer.getScore();

		if (gameState == GameState.LAST_CHANCE
				&& possibleScoreOfCurrPlayer < scoreOfLeader)

			UI.setScoreButtonState(false);
		else
			UI.setScoreButtonState(true);

	}

	private boolean shouldHighlight(int index) {
		// Gets the pairs (if any)
		int[] valuesOfPairs = dM.findPairs(index, DieManager.ABS_VALUE_FLAG,
				false);

		// True if the value of the pairs is 0
		boolean isZero = Scorer.calculate(valuesOfPairs, false, UI) == 0;

		int[] diceOnTable = dM.getDiceOnTable(DieManager.ABS_VALUE_FLAG);
		// True is there are three pairs on the table
		boolean isThreePair = Scorer.isThreePair(diceOnTable, UI, true) != 0;
		// True if there is a straight in the table
		boolean isStraight = Scorer.isStraight(diceOnTable, UI, true) != 0;

		return (!isZero || isThreePair || isStraight);

	}

	public void onRoll() {

		if (gameState == GameState.PLAYER_CONTROLLING) {
			pickUpDice();
		}

		// ROLL DICE
		// TODO alert the listeners
		setState(GameState.PLAYER_CONTROLLING);
		dM.rollDice();

		UI.animateImages();
		updateScoreBox();
		disableButtons();
	}

	private void disableButtons() {
		rollState = UI.getRollButtonState();
		scoreState = UI.getScoreButtonState();
		UI.setRollButtonState(false);
		UI.setScoreButtonState(false);
	}

	private void pickUpDice() {
		// TODO alert the listeners
		int[] highlightedDice = dM
				.getHighlightedDice(DieManager.ABS_VALUE_FLAG);

		final int scoreOnTable = Scorer.calculate(highlightedDice, true, UI);

		increaseTempScore(scoreOnTable);
		highlightedDice = dM.getHighlightedDice(DieManager.INDEX_FLAG);
		dM.pickUp(highlightedDice);

		UI.setRollButtonState(false);
		UI.setScoreButtonState(false);

	}

	public void onAnimationEnd(View v) {

		if (isFarkle() && dM.numOnTable() != 0) {
			UI.showFarkle();
			dM.clearTable();
			currPlayer.resetInRoundScore();
			if (currPlayer.getNumOfFarkles() == 3)
				currPlayer.setInRoundScore(-1000);

			return;
		}

		// Farkle Animation has ended
		if (dM.numOnTable() == 0) {
			UI.setRollButtonState(true);
			UI.setScoreButtonState(false);
			endRound();
		} else {
			// Regular animation has ended
			restoreButtons();
		}
	}

	private void restoreButtons() {
		UI.setRollButtonState(rollState);
		UI.setScoreButtonState(scoreState);
	}

	private boolean isFarkle() {
		final int[] diceOnTable = dM.getDiceOnTable(DieManager.ABS_VALUE_FLAG);
		return (Scorer.calculate(diceOnTable, true, UI) == 0);
	}

	public void onScore() {

		currPlayer.resetNumOfFarkles();

		// Values of highlighted dice on table
		final int[] highlighted = dM
				.getHighlightedDice(DieManager.ABS_VALUE_FLAG);
		// Score of the highlighted dice
		final int scoreOnTable = Scorer.calculate(highlighted, false, UI);

		// Label for the original winner
		final boolean wasThereALeader = isLeaderPastWinningScore();
		currPlayer.incrementScore(currPlayer.getInRoundScore() + scoreOnTable);
		if (isLeaderPastWinningScore() && !wasThereALeader) {
			currPlayer.setOriginalWinner(true);
		}

		endRound();
	}

	private void increaseTempScore(int scoreOnTable) {
		currPlayer.incrementInRoundScore(scoreOnTable);
		// TODO show animation of the score getting bigger
	}

	public void addListener(GameEventsListener l) {
		listeners.add(l);
	}

	public int getImage(int index) {
		return dM.getImage(index);
	}

	private void setup() {
		// SetupPlayers

		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(UI);

		final String stringOfNum = prefs.getString(
				UI.getString(R.string.PREF_number_of_players), "2");

		int numOfPlayers = Integer.valueOf(stringOfNum);

		for (int i = 1; i <= numOfPlayers; i++) {
			String temp = prefs.getString("player" + i + "NamePref", "Player"
					+ i);
			players.add(new Player(temp));
			// Set the name of the player in the GameScreen
		}

		WINNING_SCORE = Integer.valueOf(prefs
				.getString("winScorePref", "10000"));
	}

	private boolean isLeaderPastWinningScore() {
		final int scoreOfLeader = getLeader().getScore();
		return (scoreOfLeader >= WINNING_SCORE);
	}

	private Player getLeader() {
		return Player.getLeader(players);
	}

	public void newUI(GameScreen gameScreen) {
		UI = gameScreen;
		updateScoreBox();
	}

	// Updates the ScoreBox
	private void updateScoreBox() {

		for (Player p : players) {
			final int playerNum = players.indexOf(p) + 1;

			UI.updateFarkleImages(playerNum, p.getNumOfFarkles());
			UI.updatePlayerScore(playerNum, "" + p.getScore());
			UI.setPlayerName(playerNum, p.getName(), p.equals(currPlayer));

			int newTempScore = p.getInRoundScore();
			if (newTempScore == 0) {

				UI.updatePlayerTempScore(playerNum, "");
			} else {

				final String sign = (newTempScore > 0) ? "+" : "";
				UI.updatePlayerTempScore(playerNum, sign + newTempScore);
			}

		}

		UI.hideRows(players.size());
	}

	public boolean shouldAnimate(int index) {
		return (dM.getValue(index, DieManager.VALUE_FLAG) > 0);
	}

}
